#pragma once
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <grpcpp/grpcpp.h>
#include <memory>
#include <message.grpc.pb.h>
#include <mutex>
#include <queue>
#include <string>
template <typename S> class RpcConnectPool {

private:
  std::atomic<bool> _b_stop;
  std::size_t _poolSize;
  std::string _host;
  std::string _port;
  std::queue<std::unique_ptr<typename S::Stub>> _conncections;
  std::mutex _mutex;
  std::condition_variable _cond;

public:
  RpcConnectPool(std::size_t size, std::string host, std::string port)
      : _poolSize(size), _host(host), _port(port), _b_stop(false) {
    std::shared_ptr<grpc::Channel> channel = grpc::CreateChannel(
        _host + ":" + _port, grpc::InsecureChannelCredentials());
    for (int i = 0; i < _poolSize; ++i) {
      _conncections.push(std::move(S::NewStub(channel)));
    }
  }

  ~RpcConnectPool() {
    std::lock_guard<std::mutex> lock(_mutex);
    Close();
    while (!_conncections.empty()) {
      _conncections.pop();
    }
  }
  void Close() {
    _b_stop = true;
    _cond.notify_all();
  }
  std::unique_ptr<typename S::Stub> GetConnection() {
    std::unique_lock<std::mutex> lock(_mutex);
    _cond.wait(lock, [this]() { return _b_stop || !_conncections.empty(); });
    if (_b_stop) {
      return nullptr;
    }
    auto con = std::move(_conncections.front());
    _conncections.pop();
    return con;
  }
  void returnConnection(std::unique_ptr<typename S::Stub> con) {
    std::lock_guard<std::mutex> lock(_mutex);
    if (_b_stop) {
      return;
    }
    _conncections.push(std::move(con));
    // 通知相关线程
    _cond.notify_one();
  }
};